#pragma once
#include "Util.hpp"
#include "Log.hpp"
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>

#define SEP ": "
#define OK 200
#define NOT_FIND 404
#define BAD_REQUEST 400
#define SERVER_ERR 500
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

// class Code2Desc{
//   private:
//     std::unordered_map<int, std::string> _code2_desc;
//   public:
//     Code2Desc();
//     void InitCode2Desc(){
//       _code2_desc.insert({200, "OK"});
//     }
//     ~Code2Desc();
// };

static std::string Code2Desc(int code)
{
  std::string desc;
  switch (code)
  {
  case 200:
    desc = "OK";
    break;
  case 404:
    desc = "NOT_FIND";
    break;
  default:
    break;
  }
  return desc;
}

static std::string Suffix2Desc(const std::string &suffix)
{
  std::string desc;
  static std::unordered_map<std::string, std::string> suffix2desc = {
      {".html", "text/html"}, {".css", "text/css"}, {".js", "application/x-javascript"}, {".jpg", "application/x-jpg"}, {".xml", "application/xml"}};
  // return suffix2desc[suffix];
  auto iter = suffix2desc.find(suffix);
  if (iter != suffix2desc.end())
  {
    return iter->second;
  }
  else
  {
    return "text/html";
  }
}

class HttpRequest
{
public:
  std::unordered_map<std::string, std::string> _header_kv;

  std::string _request_line;
  std::vector<std::string> _request_header;
  std::string _blank;
  std::string _request_body;

  // 解析完毕的结果
  std::string _method;  // 请求方法
  std::string _uri;     // 请求资源 path?args
  std::string _version; // 请求版本
  int _content_length;
  std::string _path;
  std::string _suffix;
  std::string _query_string;

  bool _cgi;

public:
  HttpRequest() : _content_length(0), _cgi(0){};
  ~HttpRequest(){};
};

class HttpResponse
{
private:
public:
  std::string _status_line;
  std::vector<std::string> _response_header;
  std::string _blank;
  std::string _response_body;

  int _status_code;
  int _fd;
  int _body_size;

public:
  HttpResponse() : _blank(LINE_END), _status_code(OK), _fd(-1), _body_size(0) {}
  ~HttpResponse(){};
};

// 读取请求，分析请求，构建相应
// IO通信
class EndPoint
{
private:
  bool RecvHttpRequestLine();
  bool RecvHttpRequestHeader();
  bool IsNeedRecvHttpRequestBody();
  bool RecvHttpRequestBody();

  int ProcessNonCgi();
  int ProcessCgi();

  void ParseHttpRequestLine();
  void ParseHttpRequestHeader();
  void BuildHttpResponseHelper();
  void HandlerErrno();
  void BuildOkResponse();

public:
  EndPoint(int sock) : _sock(sock), _stop(false) {}
  void RecvHttpRequest();
  void BuildHttpResponse();
  void SendHttpResponse();

  bool Stop() { return _stop; }
  ~EndPoint() { close(_sock); }

private:
  int _sock;
  bool _stop;
  HttpRequest _http_request;
  HttpResponse _http_response;

  // 状态码和状态码描述映射表
};

bool EndPoint::RecvHttpRequestLine()
{
  int size = Util::ReadLine(_sock, _http_request._request_line);
  if (size > 0)
  {
    _http_request._request_line.resize(size - 1);
    LOG(INFO, _http_request._request_line);
  }
  else
  {
    _stop = true;
  }
  return _stop;
  // test
  // copy.resize(copy.size() - 1);
  // LOG(INFO, copy);
}

bool EndPoint::RecvHttpRequestHeader()
{
  std::string line;
  std::string copy;
  while (line != "\n")
  {
    line.clear();
    copy.clear();
    if (Util::ReadLine(_sock, line) <= 0)
    {
      _stop = true;
      break;
    }
    copy = line;
    copy.resize(copy.size() - 1);
    if (copy.size() == 0)
    {
      break;
    }
    _http_request._request_header.push_back(copy);
    // test
    std::string copy = line;
    copy.resize(copy.size() - 1);
    LOG(INFO, copy);
  }
  if (line == "\n")
  {
    _http_request._blank = line;
  }
  return _stop;
}

bool EndPoint::IsNeedRecvHttpRequestBody()
{
  const auto &method = _http_request._method;
  if (method == "POST")
  {
    const auto &header_kv = _http_request._header_kv;
    auto iter = header_kv.find("Content-Length");
    if (iter != header_kv.end())
    {
      _http_request._content_length = atoi(iter->second.c_str());
      LOG(INFO, "Need recv httprequest body");
      return true;
    }
  }
  return false;
}

bool EndPoint::RecvHttpRequestBody()
{
  if (IsNeedRecvHttpRequestBody())
  {
    LOG(INFO, "begin recv request body");
    int content_length = _http_request._content_length;
    auto &body = _http_request._request_body;
    char ch = 0;
    std::cout << "content_length = " << content_length << std::endl;
    while (content_length)
    {
      ssize_t size = recv(_sock, &ch, 1, 0);
      if (size > 0 && content_length > 0)
      {
        body.push_back(ch);
        content_length--;
      }
      else
      {
        _stop = true;
        break;
      }
    }
    LOG(INFO, body);
  }
  return _stop;
}

void EndPoint::RecvHttpRequest()
{
  if (RecvHttpRequestLine() || RecvHttpRequestHeader())
  {
    return;
  }
  else
  {
    ParseHttpRequestLine();
    ParseHttpRequestHeader();
    RecvHttpRequestBody();
  }
}

void EndPoint::ParseHttpRequestLine()
{
  auto &line = _http_request._request_line;
  std::stringstream ss(line);
  ss >> _http_request._method >> _http_request._uri >> _http_request._version;
  auto &method = _http_request._method;
  std::transform(method.begin(), method.end(), method.begin(), ::toupper);
  // LOG(INFO, _http_request._method);
  // LOG(INFO, _http_request._uri);
  // LOG(INFO, _http_request._version);
}

void EndPoint::ParseHttpRequestHeader()
{
  for (auto &iter : _http_request._request_header)
  {
    std::string key;
    std::string value;
    if (Util::CutString(iter, key, value, SEP))
    {
      std::cout << "debug :" << key << std::endl;
      std::cout << "debug :" << value << std::endl;
      _http_request._header_kv.insert(make_pair(key, value));
    }
    else
    {
      LOG(ERRNO, iter);
    }
  }
}

int EndPoint::ProcessNonCgi()
{
  // auto& size = _http_response._body_size;
  _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
  if (_http_response._fd < 0)
  {
    LOG(ERRNO, _http_request._path + " not find");
    return NOT_FIND;
  }
  LOG(INFO, _http_request._path + " open success");
  return OK;
  //_http_response._status_code = OK;
  // if (_http_response._fd > 0){
  //  _http_response._status_line = HTTP_VERSION;
  //  _http_response._status_line += " ";
  //  _http_response._status_line += std::to_string(_http_response._status_code);
  //  _http_response._status_line += " ";
  //  _http_response._status_line += Code2Desc(_http_response._status_code);
  //  _http_response._status_line += LINE_END;

  //  std::string header_line = "Content-Type: ";
  //  header_line += Suffix2Desc(_http_request._suffix);
  //  header_line += LINE_END;
  //  _http_response._response_header.push_back(header_line);

  //  header_line = "Content-Length: ";
  //  header_line += std::to_string(size);
  //  header_line += LINE_END;
  //  _http_response._response_header.push_back(header_line);
  //  return OK;
  //}
  // return NOT_FIND;
}

int EndPoint::ProcessCgi()
{
  LOG(INFO, "process cgi mthod");
  // std::cout << "debug Cgi function" << std::endl;
  // 创建子线程去执行Cig程序，主线程自始至终不可以改变,因为httpserver是服务器
  auto &code = _http_response._status_code;
  auto &bin = _http_request._path;
  auto &method = _http_request._method;
  auto &body_text = _http_request._request_body;
  auto &query_string = _http_request._query_string;
  auto &response_body = _http_response._response_body;
  int input[2];
  int output[2];

  if (pipe(input) < 0)
  {
    code = SERVER_ERR;
    LOG(ERRNO, "pipe create failed");
    return SERVER_ERR;
  }
  if (pipe(output) < 0)
  {
    code = SERVER_ERR;
    LOG(ERRNO, "pipe create failed");
    return SERVER_ERR;
  }

  std::cout << "method = " << method << std::endl;
  std::cout << "bin" << bin << std::endl;
  // std::cout << "_http_request._query_string = " << _http_request._query_string << std::endl;
  std::string query_string_env;
  std::string method_env = "METHOD_ENV=";
  method_env += method;
  putenv((char *)method_env.c_str());
  int content_length = _http_request._content_length;
  std::string content_length_env;
  if (method == "GET")
  {
    query_string_env = "QUERY_STRING=";
    query_string_env += query_string;
    putenv((char *)query_string_env.c_str());
  }
  else if (method == "POST")
  {
    content_length_env += "CONTENT_LENGTH=";
    content_length_env += std::to_string(content_length);
    putenv((char *)content_length_env.c_str());
  }
  pid_t pid = fork();
  if (pid == 0)
  {
    // 子线程 执行Cig程序
    close(input[0]);
    close(output[1]);
    LOG(INFO, "create child process success");
    // 关闭标准输入输出/改为读和写
    dup2(output[0], 0);
    dup2(input[1], 1);
    execl(bin.c_str(), bin.c_str(), nullptr);
    exit(1);
  }
  else if (pid < 0)
  {
    LOG(ERRNO, "fock errno");
    return NOT_FIND;
  }
  else
  {
    // 父进程接收子线程结果
    close(input[1]);
    close(output[0]);

    if (method == "POST")
    {
      const char *start = body_text.c_str();
      int size = 0;   // 每次write的字节数
      size_t cur = 0; // 已经写入的字节数
      std::cout << body_text.size() << std::endl;
      while (cur < body_text.size() && ((size = write(output[1], start + cur, body_text.size() - cur)) > 0))
      {
        cur += size;
      }
      // std::cout << "cur = " << cur << std::endl;
      // std::cout << "size = " << size << std::endl;
      // std::cout << "Body_text = " << body_text.c_str();
    }
    char ch = 0;
    while (read(input[0], &ch, 1) > 0)
    {
      response_body.push_back(ch);
      _http_response._body_size = response_body.size();
    }
    int status = 0;
    pid_t ret = waitpid(pid, &status, 0);
    if (ret == pid)
    {
      if (WIFEXITED(status))
      {
        if (WEXITSTATUS(status) != 0)
        {
          code = SERVER_ERR;
        }
      }
      else
      {
        code = SERVER_ERR;
      }
    }
    else
    {
      code = SERVER_ERR;
    }
    if (code == OK)
    {
      LOG(INFO, "wait child process success");
    }
    else if (code == SERVER_ERR)
    {
      LOG(INFO, "wait child process errno");
    }
    return code;

    close(input[0]);
    close(output[1]);
  }
  return OK;
}

void EndPoint::HandlerErrno()
{
  auto &code = _http_response._status_code;
  auto &fd = _http_response._fd;
  std::string path = "./";
  path += WEB_ROOT;
  path += "/";
  _http_request._cgi = false;
  if (code == SERVER_ERR)
  {
    path += PAGE_404;
  }
  else if (code == NOT_FIND)
  {
    path += PAGE_404;
  }
  else if (code == BAD_REQUEST)
  {
    path += PAGE_404;
  }
  fd = open(path.c_str(), O_RDONLY);
  if (fd > 0)
  {
    LOG(INFO, "404 html open success");
    struct stat st;
    stat(path.c_str(), &st);
    std::string line = "Content-Type: text/html";
    line += LINE_END;
    _http_response._response_header.push_back(line);
    line = "Content-Length: ";
    line += std::to_string(st.st_size);
    _http_response._body_size = st.st_size;
    line += LINE_END;
    _http_response._response_header.push_back(line);
    LOG(INFO, path);
  }
  else
  {
    LOG(ERROR, "404 html open failed");
    LOG(ERROR, path);
  }
}

void EndPoint::BuildOkResponse()
{

  auto &size = _http_response._body_size;
  std::string header_line = "Content-Type: ";
  header_line += Suffix2Desc(_http_request._suffix);
  header_line += LINE_END;
  _http_response._response_header.push_back(header_line);

  header_line = "Content-Length: ";
  header_line += std::to_string(size);
  header_line += LINE_END;
  _http_response._response_header.push_back(header_line);
}

void EndPoint::BuildHttpResponseHelper()
{
  auto &code = _http_response._status_code;
  auto &status_line = _http_response._status_line;
  status_line += HTTP_VERSION;
  status_line += " ";
  status_line += std::to_string(code);
  status_line += " ";
  status_line += Code2Desc(code);
  status_line += LINE_END;

  switch (code)
  {
  case OK:
    BuildOkResponse();
    break;
  case NOT_FIND:
    HandlerErrno();
    break;
  case SERVER_ERR:
    HandlerErrno();
    break;
    break;
  }
}

void EndPoint::SendHttpResponse()
{
  send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
  for (auto &iter : _http_response._response_header)
  {
    send(_sock, iter.c_str(), iter.size(), 0);
  }
  send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
  if (_http_request._cgi)
  {
    auto &response_body = _http_response._response_body;
    size_t cur = 0;
    size_t size = 0;
    const char *start = response_body.c_str();
    while (cur < response_body.size() && (size = send(_sock, start + cur, response_body.size() - cur, 0)) > 0)
    {
      cur += size;
    }
  }
  else
  {
    sendfile(_sock, _http_response._fd, nullptr, _http_response._body_size);
    close(_http_response._fd);
  }
}

void EndPoint::BuildHttpResponse()
{
  std::string path;
  std::string method = _http_request._method;
  auto &size = _http_response._body_size;
  size_t found;
  auto &code = _http_response._status_code;
  // std::cout << method << std::endl;
  if (method != "GET" && method != "POST")
  {
    // 非法请求
    LOG(WARNING, "method is not right");
    code = BAD_REQUEST;
    goto END;
  }
  if (method == "GET")
  {
    size_t pos = _http_request._uri.find('?');
    if (pos != std::string::npos)
    {
      Util::CutString(_http_request._uri, _http_request._path, _http_request._query_string, "?");
      _http_request._cgi = true;
      // 1.路径是否合法 2.资源是否存在
    }
    else
    {
      _http_request._path = _http_request._uri;
    }
    // std::cout << "debug uri" << _http_request._uri << std::endl;
    // std::cout << "debug path" << _http_request._path << std::endl;
    // std::cout << "debug query_string" << _http_request._query_string << std::endl;
  }
  else if (_http_request._method == "POST")
  {
    // POST
    _http_request._cgi = true;
    _http_request._path = _http_request._uri;
  }
  path = _http_request._path;
  LOG(INFO, path);
  _http_request._path = WEB_ROOT;
  _http_request._path += path;
  LOG(INFO, _http_request._path);
  // std::cout << "debug path" << _http_request._path << std::endl;
  if (_http_request._path[_http_request._path.size() - 1] == '/')
  {
    _http_request._path += HOME_PAGE;
  }
  // std::cout << "debug " << _http_request._path << std::endl;
  struct stat st;
  if (stat(_http_request._path.c_str(), &st) == 0)
  {
    // 资源存在
    size = st.st_size;
    if (S_ISDIR(st.st_mode))
    {
      // 说明请求的资源是一个目录，是不被允许的，需要进行默认处理
      // 虽然是一个目录，但是绝对不会以/结尾
      _http_request._path += "/";
      _http_request._path += HOME_PAGE;
      stat(_http_request._path.c_str(), &st);
      if (stat(_http_request._path.c_str(), &st) == 0)
      {
        size = st.st_size;
      }
    }
    if ((st.st_mode & S_IXUSR) & (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
    {
      // 请求的是一个可执行程序，需要特殊处理
      LOG(INFO, ".exe spaciel chuli");
      _http_request._cgi = true;
    }
  }
  else
  {
    // 资源不存在
    std::string info = _http_request._path;
    info += " Not Find";
    code = NOT_FIND;
    LOG(WARNING, info);
    _http_request._cgi = false;
    _http_response._status_code = NOT_FIND;
    goto END;
  }

  // 获取path suffix
  found = _http_request._path.rfind(".");
  if (found == std::string::npos)
  {
    _http_request._suffix = ".html";
  }
  else
  {
    _http_request._suffix = _http_request._path.substr(found);
  }

  if (_http_request._cgi)
  {
    _http_response._status_code = ProcessCgi();
  }
  else
  {
    // 1.目标网页一定存在 2.返回并非网页内容，而是构建HTTP响应
    _http_response._status_code = ProcessNonCgi(); // 简单文本网页返回
  }
END:
  BuildHttpResponseHelper();

  return;
}

class CallBack
{
public:
  CallBack(){};
  ~CallBack(){};
  void operator()(int sock) { HandlerRequest(sock); }
  void HandlerRequest(int sock)
  {
    LOG(INFO, "handler request begin");
    // int sock = *(int*)args;
    // delete (int*)args;
    // std::cout << "get a new link" << sock << std::endl;

#ifdef DEBUG
    char buffer[1024 * 10] = {0};
    std::cout << "\n\n----------------------" << std::endl;
    recv(sock, buffer, sizeof(buffer) - 1, 0);
    std::cout << buffer << std::endl;
    std::cout << "\n\n----------------------" << std::endl;
#else
    EndPoint *ep = new EndPoint(sock);
    ep->RecvHttpRequest();
    if (!ep->Stop())
    {
      LOG(INFO, "Recv No Error, Begin Build and Send");
      ep->BuildHttpResponse();
      ep->SendHttpResponse();
    }
    else
    {
      LOG(WARNING, "Recv Error, Stop Build and Send");
    }
    delete ep;
#endif
    // std::string line;
    // Util::ReadLine(sock, line);
    // std::cout << line << std::endl;
    close(sock);
    LOG(INFO, "handler request end");
  }
};
